<script setup lang="ts">
import * as THREE from 'three';
import {OrbitControls} from "three/examples/jsm/controls/OrbitControls";
import {OBJLoader} from "three/examples/jsm/loaders/OBJLoader";
import * as TWEEN from '@tweenjs/tween.js';
import {GIFTexture} from 'three-gif-texture';
import {CSS2DRenderer, CSS2DObject} from "three/examples/jsm/renderers/CSS2DRenderer";
import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader';
//创建一个场景对象
const scene = new THREE.Scene()
scene.background = new THREE.Color('#ffffff');
//创建CSS2DRenderer
const labelRenderer = new CSS2DRenderer();
labelRenderer.setSize(window.innerWidth, window.innerHeight);
labelRenderer.domElement.style.position = 'absolute';
labelRenderer.domElement.style.top = '0';
labelRenderer.domElement.style.pointerEvents = 'none';
/**
 * 相机设置
 */
//窗口宽度
const width = window.innerWidth;
//窗口高度
const height = window.innerHeight;
//窗口宽高比
const k = width / height;
//三维场景显示范围控制系数，系数越大，显示的范围越大
const s = 200;
//创建相机对象
const camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 1000);
//设置相机位置
/**
 * 相机中心点
 */
const cameraCenterPoint = ref({
  x: 0,
  y: 0,
  z: 0
})
/**
 * 相机角度
 */
const cameraPoint = ref({
  x: 0,
  y: 200,
  z: 200
})
camera.position.set(cameraPoint.value.x, cameraPoint.value.y, cameraPoint.value.z);
//设置相机方向(指向的场景对象)
camera.lookAt(scene.position);
/**
 * 光源设置
 */
//新建点光源(常用光源分为点光源和环境光，点光源的效果类似灯泡，环境光的效果类似白天的太阳光)
const point = new THREE.PointLight(0xffffff);
//设置点光源的位置
point.position.set(400, 200, 300);
//将点光源添加到场景中
scene.add(point);
const objWidth = 30;
/**
 * ThreeJs创建一条管道
 */
// 创建多段线条的顶点数据
const p1 = new THREE.Vector3(50, 0, 0);
const p2 = new THREE.Vector3(objWidth / 2 + 50, 0, 0);
//曲线点
const p3 = new THREE.Vector3(objWidth / 2 + 80, 0, -3);
const p4 = new THREE.Vector3(objWidth / 2 + 85, 0, -30);
const p5 = new THREE.Vector3(objWidth / 2 + 84, 0, -80);
//曲线点
const p6 = new THREE.Vector3(objWidth / 2 + 87, 0, -110);
const p7 = new THREE.Vector3(objWidth / 2 + 114, 0, -114);
const p8 = new THREE.Vector3(objWidth / 2 + 264, 0, -114);
//曲线点
const p9 = new THREE.Vector3(objWidth / 2 + 294, 0, -111);
const p10 = new THREE.Vector3(objWidth / 2 + 298, 0, -84);
const p11 = new THREE.Vector3(objWidth / 2 + 298, 0, -30);
//曲线点
const p12 = new THREE.Vector3(objWidth / 2 + 302, 0, -3);
const p13 = new THREE.Vector3(objWidth / 2 + 328, 0, 0);
const p14 = new THREE.Vector3(objWidth / 2 + 338, 0, 0);

// 直线
const line1 = new THREE.LineCurve3(p1, p2);
// 曲线
const line2 = new THREE.CatmullRomCurve3([p2, p3, p4]);
//直线
const line3 = new THREE.LineCurve3(p4, p5);
// 曲线
const line4 = new THREE.CatmullRomCurve3([p5, p6, p7]);
//直线
const line5 = new THREE.LineCurve3(p7, p8);
// 曲线
const line6 = new THREE.CatmullRomCurve3([p8, p9, p10]);
//直线
const line7 = new THREE.LineCurve3(p10, p11);
// 曲线
const line8 = new THREE.CatmullRomCurve3([p11, p12, p13]);
//直线
const line9 = new THREE.LineCurve3(p13, p14);
// 创建CurvePath对象
const CurvePath = new THREE.CurvePath() as any;
// 插入多段线条
CurvePath.curves.push(line1, line2, line3, line4, line5, line6, line7, line8, line9);
//通过多段曲线路径创建生成管道，CCurvePath：管道路径
const geometry1 = new THREE.TubeGeometry(CurvePath, 100, 5, 25, false);
//材质对象Material
const material1 = new THREE.MeshLambertMaterial({
  color: '#f8a831',
  side: THREE.DoubleSide
});
//网格模型对象Mesh
const mesh1 = new THREE.Mesh(geometry1, material1);
//网格模型添加到场景中
scene.add(mesh1);
/**
 * 第二根管
 */
const l2p1 = new THREE.Vector3(0, -80, 50);
const l2p2 = new THREE.Vector3(0, -80, objWidth / 2 + 50);
//曲线点
const l2p3 = new THREE.Vector3(-3, -80, objWidth / 2 + 80);
const l2p4 = new THREE.Vector3(-30, -80, objWidth / 2 + 85);
const l2p5 = new THREE.Vector3(-400, -80, objWidth / 2 + 84);
// 直线
const l2line1 = new THREE.LineCurve3(l2p1, l2p2);
// 曲线
const l2line2 = new THREE.CatmullRomCurve3([l2p2, l2p3, l2p4]);
// 直线
const l2line3 = new THREE.LineCurve3(l2p4, l2p5);
// 创建CurvePath对象
const CurvePath2 = new THREE.CurvePath() as any;
// 插入多段线条
CurvePath2.curves.push(l2line1, l2line2, l2line3);
//通过多段曲线路径创建生成管道，CCurvePath：管道路径
const geometry2 = new THREE.TubeGeometry(CurvePath2, 100, 5, 25, false);
//材质对象Material
const material2 = new THREE.MeshLambertMaterial({
  color: '#315ff8',
  side: THREE.DoubleSide
});
//网格模型对象Mesh
const mesh2 = new THREE.Mesh(geometry2, material2);
//网格模型添加到场景中
scene.add(mesh2);
/**
 * 第三根管
 */
const l3p1 = new THREE.Vector3(-50, 0, 0);
const l3p2 = new THREE.Vector3(-400, 0, 0);
// 直线
const l3line1 = new THREE.LineCurve3(l3p1, l3p2);
// 创建CurvePath对象
const CurvePath3 = new THREE.CurvePath() as any;
// 插入多段线条
CurvePath3.curves.push(l3line1);
//通过多段曲线路径创建生成管道，CCurvePath：管道路径
const geometry3 = new THREE.TubeGeometry(CurvePath3, 100, 5, 25, false);
//材质对象Material
const material3 = new THREE.MeshLambertMaterial({
  color: '#f8a831',
  side: THREE.DoubleSide
});
//网格模型对象Mesh
const mesh3 = new THREE.Mesh(geometry3, material3);
//网格模型添加到场景中
scene.add(mesh3);
/**
 * 第四根管
 */
// 创建多段线条的顶点数据
const l4p1 = new THREE.Vector3(40, -50, -20);
const l4p2 = new THREE.Vector3(objWidth / 2 + 40, -50, -20);
//曲线点
const l4p3 = new THREE.Vector3(objWidth / 2 + 70, -47, -20);
const l4p4 = new THREE.Vector3(objWidth / 2 + 73, -20, -20);
const l4p5 = new THREE.Vector3(objWidth / 2 + 73, 0, -20);
//曲线点
const l4p6 = new THREE.Vector3(objWidth / 2 + 76, 27, -20);
const l4p7 = new THREE.Vector3(objWidth / 2 + 103, 30, -20);
const l4p8 = new THREE.Vector3(objWidth / 2 + 338, 30, -20);
// 直线
const l4line1 = new THREE.LineCurve3(l4p1, l4p2);
// 曲线
const l4line2 = new THREE.CatmullRomCurve3([l4p2, l4p3, l4p4]);
// 直线
const l4line3 = new THREE.LineCurve3(l4p4, l4p5);
// 曲线
const l4line4 = new THREE.CatmullRomCurve3([l4p5, l4p6, l4p7]);
// 直线
const l4line5 = new THREE.LineCurve3(l4p7, l4p8);
// 创建CurvePath对象
const CurvePath4 = new THREE.CurvePath() as any;
// 插入多段线条
CurvePath4.curves.push(l4line1, l4line2, l4line3, l4line4, l4line5);
//通过多段曲线路径创建生成管道，CCurvePath：管道路径
const geometry4 = new THREE.TubeGeometry(CurvePath4, 100, 5, 25, false);
//材质对象Material
const material4 = new THREE.MeshLambertMaterial({
  color: '#f8a831',
  side: THREE.DoubleSide
});
//网格模型对象Mesh
const mesh4 = new THREE.Mesh(geometry4, material4);
//网格模型添加到场景中
scene.add(mesh4);
/**
 * 创建渲染器对象
 */
//创建渲染器对象(渲染要等所有东西都加进去再渲染场景，创建和3D模型和光源没有先后顺序)
const renderer = new THREE.WebGLRenderer();
//循环渲染
const render = () => {
  //执行渲染操作   指定场景、相机作为参数
  renderer.render(scene, camera);
  labelRenderer.render(scene, camera);
  // 使用 requestAnimationFrame 函数安排下一次渲染
  requestAnimationFrame(render);
}
/**
 * 开启场景镜头控制
 */
const controls_ = new OrbitControls(camera, renderer.domElement);

const controls = () => {
  controls_.addEventListener('change', function () {
    // 浏览器控制台查看相机位置变化
    // console.log('camera.position', camera.position);
  });
  controls_.enablePan = true;
  controls_.minZoom = 0.1;
  controls_.maxZoom = 3;
}

/**
 * 加载锅炉模型
 */
const loadDeviceObj = () => {
  //创建一个用于加载 .obj 资源的加载器
  const OBJLoader_ = new FBXLoader();
  // const loader = new FBXLoader();
  OBJLoader_.load('/Three/models/5.fbx', function (obj: any) {
    scene.add(obj);
    // 可选：调整模型位置、缩放等
    obj.scale.set(0.2, 0.2, 0.5);
    obj.position.set(0, 0, 0);
    // let mesh = obj.children[0];
    // mesh.rotateZ(Math.PI);
    // mesh.material = new THREE.MeshBasicMaterial({
    //   map: new THREE.TextureLoader().load('/Three/models/002.png'),
    //   side: THREE.DoubleSide,
    //   transparent: true,
    //   clipIntersection: true,
    // });
    // mesh.translateY(-100);
    // mesh.translateZ(0);
    // mesh.scale.x = 5;
    // mesh.scale.y = 10;
    // mesh.scale.z = 5;
    // mesh['data'] = {type: 'device'};
    // scene.add(mesh);
  });
}

const animate = () => {
  // 切换相机位置必须要在这里执行这一步
  TWEEN.update();
  renderer.render(scene, camera);
  labelRenderer.render(scene, camera);
  requestAnimationFrame(animate);
}
/**
 * 相机平滑过度
 */
const animateCamera = (toPoint: any) => {
  const c = cameraCenterPoint.value;
  const tween = new TWEEN.Tween({
    x1: cameraPoint.value.x, // 相机x
    y1: cameraPoint.value.y, // 相机y
    z1: cameraPoint.value.z, // 相机z
    x2: c.x, // 控制点的中心点x
    y2: c.y, // 控制点的中心点y
    z2: c.z  // 控制点的中心点z
  }).to({
    x1: toPoint.x,
    y1: toPoint.y,
    z1: toPoint.z,
    x2: c.x,
    y2: c.y,
    z2: c.z
  }, 1000)
      .easing(TWEEN.Easing.Cubic.InOut)
      .onUpdate(function (object) {
        camera.position.set(object.x1, object.y1, object.z1)
        controls_.target.x = object.x2;
        controls_.target.y = object.y2;
        controls_.target.z = object.z2;
        controls_.update();
      }).start();
  animate();
  cameraPoint.value = toPoint;
}
const lastMesh = ref({} as any);
const params = reactive({
  left: 0,
  top: 0,
  S: 0
})
/**
 * 模型点击选择
 * @param event
 */
renderer.localClippingEnabled = true;
const PlaneArr = [new THREE.Plane(new THREE.Vector3(-1, 0, -2), 1), new THREE.Plane(new THREE.Vector3(1, 0, -2), 0)];
const choose = (event: any) => {
  if (!_.isEmpty(lastMesh.value)) {
    lastMesh.value.material.clippingPlanes = null;
  }
  const Sx = event.clientX;
  const Sy = event.clientY;
  params.left = Sx + 20;
  params.top = Sy + 20;
  const x = (Sx / window.innerWidth) * 2 - 1;
  const y = -(Sy / window.innerHeight) * 2 + 1;
  const raycaster = new THREE.Raycaster();
  raycaster.setFromCamera(new THREE.Vector2(x, y), camera);
  const intersects = raycaster.intersectObjects(scene.children, true);
  if (intersects.length > 0) {
    const mesh = intersects[0].object as any;
    scene.updateMatrixWorld(true);
    const worldPosition = new THREE.Vector3();
    mesh.getWorldPosition(worldPosition);
    lastMesh.value = mesh;
    PlaneArr[0].constant = worldPosition.x;
    PlaneArr[1].constant = worldPosition.z;
    mesh.material.clipIntersection = true;
    mesh.material.clippingPlanes = PlaneArr;
  }
}
addEventListener('click', choose);
/**
 * 加载火焰gif
 */
const loadGif = () => {
// 加载GIF图像
  new GIFTexture('/Three/images/fire.gif', 'autoDraw', map => {
    const mesh = new THREE.Mesh(
        new THREE.PlaneGeometry(100, 200),
        new THREE.MeshStandardMaterial({map})
    );
    mesh.position.x = 0;
    scene.add(mesh);
  })
}

/**
 * 创建位号显示
 */
const createCSS2DObject = (point: any, text: string) => {
  let el = document.createElement('div');
  el.innerHTML = `<div style="position:relative;width: 100px;white-space: nowrap;">
        <div style="position: absolute;top:-25px;left:20px;width:100px;color:#000000;">
        ${text}
        </div>
        <div style="position: absolute;top:0;left:0;">
          <img src="/Three/images/line/line.png">
        </div>
      </div>`
  el.cloneNode(true);
  el.style.pointerEvents = 'none';
  const css2DObject = new CSS2DObject(el);
  css2DObject.position.set(point.x, point.y, point.z);
  scene.add(css2DObject)
}

const createTabNumber = () => {
  createCSS2DObject({x: 67, y: -20, z: 0}, 'T12161 - 463°C');
  createCSS2DObject({x: 67, y: -45, z: 0}, 'T12161 - 501°C');
  createCSS2DObject({x: 67, y: -70, z: 0}, 'T12161 - 537°C');
  createCSS2DObject({x: objWidth / 2 + 80, y: 20, z: -20}, 'RC2101C - 528.3Mpa');
}

onMounted(() => {
  nextTick(() => {
    loadDeviceObj();
    loadGif();
    createTabNumber();
    //设置渲染区域尺寸(设置的是整个屏幕的长度和宽度
    renderer.setSize(width, height);
    //设置渲染的背景色
    renderer.setClearColor(0xb9d3ff, 1);
    //body元素中插入canvas对象，也可以在页面中添加div，并加上id，通过id指定在某个div中渲染
    const container = document.getElementById("three") as any;
    container?.appendChild(renderer.domElement);
    container?.appendChild(labelRenderer.domElement);
    //渲染
    render();
    //开启镜头旋转
    controls();
  })
})
</script>

<template>
  <div class="three-body w-full h-full">
    <div class="handle">
      <el-button-group class="ml-4">
        <el-button type="primary" @click="animateCamera({x:0,y:0,z:200})">正面视角</el-button>
        <el-button type="primary" @click="animateCamera({x:0,y:200,z:200})">斜视视角</el-button>
        <el-button type="primary" @click="animateCamera({x:0,y:200,z:0})">俯视视角</el-button>
      </el-button-group>
    </div>
    <div id="three" class="w-full h-full"></div>
  </div>
</template>

<style scoped lang="scss">
.three-body {
  position: relative;

  .handle {
    position: absolute;
    top: 10px;
    right: 20px;
  }
}
</style>